// CsFormatter.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Metaspec;

namespace Metaspec
{
	//------------------------------------------------------------------------------
	// CsFormatTokenType
	//------------------------------------------------------------------------------

	enum CsFormatTokenType : byte
	{
		tkF_WS = CsTokenType.TOKEN_COUNT,
		tkF_WS_KILL,
		tkF_NEWLINE,
		tkF_NEWLINE_KILL,
		tkF_INC_TAB,
		tkF_DEC_TAB,
		tkF_PP_INC_TAB,
		tkF_PP_DEC_TAB,
	};

	//------------------------------------------------------------------------------
	// CsFormatter
	//------------------------------------------------------------------------------

	public class CsFormatter : ICsAstCallback
	{
		enum generation_option
		{
			go_generate_tokens,
			go_generate_text,
		};

		public CsFormatter()
		{
			storeNewlines_ = true;
			tab_token_buffer_ = null;
			tab_token_buffer_size_ = 0;
			ws_token_buffer_ = null;
			ws_token_buffer_size_ = 0;
			setDefault();
		}
		//~CsFormatter();
		void setDefault()
		{
			//compound_identifier_before_after.Clear();
			compound_identifier_before_after = new List<CsToken>();
			//compound_identifier_around_dot.Clear();
			compound_identifier_around_dot = new List<CsToken>();
			keyword_around = new List<CsToken>();
			keyword_around.Add(new CsToken((CsTokenType)(byte)CsFormatTokenType.tkF_WS, 1, new LineInfo()));
			//operator_around.Clear();
			operator_around = new List<CsToken>();
			operator_around.Add(new CsToken((CsTokenType)(byte)CsFormatTokenType.tkF_WS, 1, new LineInfo()));
			end_of_line_type_ = LineEnum.x0Dx0A;
			generation_option_ = generation_option.go_generate_text;
		}

		public void generate(CsNode node)
		{
			//tokens_.Clear();
			tokens_ = new List<CsToken>();
			//generated_text_.Clear();
			//generated_text_ = string.Empty;
			generated_text_ = new StringBuilder();
			n_tab = 0;
			//#ifdef _DEBUG
			//	bCheckPushBack = true;
			//#endif // _DEBUG
			addNewlineKill0();
			node.callback(this);
			addNewlineKill1();
			//#ifdef _DEBUG
			//	bCheckPushBack = false;
			//#endif // _DEBUG

			//MediumFastDeque<CsToken>::iterator it = tokens_.begin();
			//MediumFastDeque<CsToken>::iterator it_end = tokens_.end();
			int it = 0;
			int it_end = tokens_.Count;
			while (it != it_end)
			{
				//#ifdef _DEBUG
				//		size_t index = std::distance ( tokens_.begin(), it );
				//#endif // _DEBUG

				int n_ws_min = 0;
				int n_ws_max = int.MaxValue;
				int n_newline_min = 0;
				int n_newline_max = int.MaxValue;

				while (it != it_end
					&& ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_WS
						|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_WS_KILL
						|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_NEWLINE
						|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_NEWLINE_KILL
						|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_INC_TAB
						|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_DEC_TAB))
				{
				// ws

		ws_loop:
					while (it != it_end
						&& ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_WS
							|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_WS_KILL))
					{
						if ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_WS)
						{
							n_ws_min = Math.Max(n_ws_min, tokens_[it].number);
							++it;
							goto ws_loop;
						}
						if ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_WS_KILL)
						{
							n_ws_max = Math.Min(n_ws_max, tokens_[it].number);
							++it;
							goto ws_loop;
						}
					}

					// newline

		newline_loop:
					if (it != it_end
						&& ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_NEWLINE
							|| (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_NEWLINE_KILL))
					{
						if ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_NEWLINE)
						{
							n_newline_min = Math.Max(n_newline_min, (int)tokens_[it].number);
							//n_ws_max = 0;
							++it;
							goto newline_loop;
						}
						if ((byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_NEWLINE_KILL)
						{
							n_newline_max = Math.Min(n_newline_max, (int)tokens_[it].number);
							++it;
							goto newline_loop;
						}
					}

					if (it != it_end
						&& (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_INC_TAB)
					{
						++n_tab;
						++it;
					}
					if (it != it_end
						&& (byte)tokens_[it].type == (byte)CsFormatTokenType.tkF_DEC_TAB)
					{
						--n_tab;
						++it;
					}
				}

				int n_newline = Math.Min(n_newline_min, n_newline_max);
				if (n_newline != 0)
					n_ws_max = 0;
				for (int i = 0; i != n_newline; ++i)
				{
					if (generation_option_ == generation_option.go_generate_text)
					{
						switch (end_of_line_type_)
						{
							case LineEnum.x0Dx0A:
							default:
								generated_text_.Append("\r");
								generated_text_.Append("\n");
								break;
							case LineEnum.x0A:
								generated_text_.Append("\n");
								break;
							case LineEnum.x0D:
								generated_text_.Append("\r");
								break;
						}
					}
					else if (generation_option_ == generation_option.go_generate_tokens)
					{
						generated_tokens_.Add
							(new CsToken(CsTokenType.tkNEW_LINE, end_of_line_type_, new LineInfo()));
					}
					else
						Debug.Assert(false);
					if (generation_option_ == generation_option.go_generate_text)
					{
						for (int j = 0; j != n_tab; ++j)
						{
							generated_text_.Append("\t");
						}
					}
					else if (generation_option_ == generation_option.go_generate_text)
					{
						//if (n_tab > tab_token_buffer_size_)
						//{
						//  while (n_tab > tab_token_buffer_size_)
						//  {
						//    if (tab_token_buffer_size_ == 0)
						//      tab_token_buffer_size_ = tab_token_buffer_size_start_;
						//    else
						//      tab_token_buffer_size_ *= 2;
						//  }
						//  //delete[] tab_token_buffer_;
						//  //tab_token_buffer_ = new char[tab_token_buffer_size_];
						//  //memset ( tab_token_buffer_, '\t', tab_token_buffer_size_ );
						//}
						generated_tokens_.Add
							(new CsToken(CsTokenType.tkWS, new LineInfo(), tab_token_buffer_.Substring(0, n_tab)));
					}
					else
						Debug.Assert(false);
				}

				int n_ws = Math.Min(n_ws_min, n_ws_max);
				if (generation_option_ == generation_option.go_generate_text)
				{
					for (int i = 0; i != n_ws; ++i)
					{
						generated_text_.Append(" ");
					}
				}
				else if (generation_option_ == generation_option.go_generate_text)
				{
					//if (n_ws > ws_token_buffer_size_)
					//{
					//  while (n_ws > ws_token_buffer_size_)
					//  {
					//    if (ws_token_buffer_size_ == 0)
					//      ws_token_buffer_size_ = ws_token_buffer_size_start_;
					//    else
					//      ws_token_buffer_size_ *= 2;
					//  }
					//  //delete[] ws_token_buffer_;
					//  //ws_token_buffer_ = new char[ws_token_buffer_size_];
					//  //memset ( ws_token_buffer_, ' ', ws_token_buffer_size_ );
					//}
					generated_tokens_.Add
						(new CsToken(CsTokenType.tkWS, new LineInfo(), ws_token_buffer_.Substring(0, n_ws)));
				}
				else
					Debug.Assert(false);

				if (it != it_end)
				{
					if (generation_option_ == generation_option.go_generate_text)
					{
						string text = tokens_[it].getOriginalText();
						if (text != null)
						{
							generated_text_.Append(text);
						}
					}
					else if (generation_option_ == generation_option.go_generate_tokens)
					{
						generated_tokens_.Add(tokens_[it]);
					}
					else
						Debug.Assert(false);
					++it;
				}
			}
		}

		//void generate_text();
		//void generate_tokens();

		// callback
		public void callback(CsQualifiedIdentifier compound_identifier)
		{
			addTokens(compound_identifier_before_after);
			bool bFirst = true;
			foreach (CsQualifiedIdentifierPart id in compound_identifier)
			{
				if (!bFirst)
				{
					addTokens(compound_identifier_around_dot);
					addToken(CsTokenType.tkDOT);
					addTokens(compound_identifier_around_dot);
				}
				addToken(id.identifier.identifier);
				bFirst = false;
			}
			addTokens(compound_identifier_before_after);
		}
		public void callback(CsModifiers node)
		{
			uint flag = 1;
			while (flag <= (uint)CsModifierEnum.mUNSAFE)
			{
				if ((node.flags & flag) != 0)
				{
					switch (flag)
					{
						case (uint)CsModifierEnum.mINTERNAL:
							addKeyword(CsTokenType.tkINTERNAL);
							break;
						case (uint)CsModifierEnum.mPRIVATE:
							addKeyword(CsTokenType.tkPRIVATE);
							break;
						case (uint)CsModifierEnum.mPROTECTED:
							addKeyword(CsTokenType.tkPROTECTED);
							break;
						case (uint)CsModifierEnum.mPUBLIC:
							addKeyword(CsTokenType.tkPUBLIC);
							break;
						case (uint)CsModifierEnum.mABSTRACT:
							addKeyword(CsTokenType.tkABSTRACT);
							break;
						case (uint)CsModifierEnum.mEXTERN:
							addKeyword(CsTokenType.tkEXTERN);
							break;
						case (uint)CsModifierEnum.mNEW:
							addKeyword(CsTokenType.tkNEW);
							break;
						case (uint)CsModifierEnum.mOVERRIDE:
							addKeyword(CsTokenType.tkOVERRIDE);
							break;
						case (uint)CsModifierEnum.mREADONLY:
							addKeyword(CsTokenType.tkREADONLY);
							break;
						case (uint)CsModifierEnum.mSEALED:
							addKeyword(CsTokenType.tkSEALED);
							break;
						case (uint)CsModifierEnum.mSTATIC:
							addKeyword(CsTokenType.tkSTATIC);
							break;
						case (uint)CsModifierEnum.mVIRTUAL:
							addKeyword(CsTokenType.tkVIRTUAL);
							break;
						case (uint)CsModifierEnum.mVOLATILE:
							addKeyword(CsTokenType.tkVOLATILE);
							break;
						case (uint)CsModifierEnum.mUNSAFE:
							addKeyword(CsTokenType.tkUNSAFE);
							break;
						default:
#if !PUBLIC_RELEASE
							Debug.Assert(false);
#else
					ASSERT(0);
#endif // !PUBLIC_RELEASE
							break;
					}
				}
				flag <<= 1;
			}
			if ((node.flags & (uint)CsModifierEnum.mPARTIAL) != 0)
			{
				addToken("partial");
			}
		}
		public void callback(CsTypeSpecifier node)
		{
			switch (node.specifier_type)
			{
				case TypeSpecifierEnum.tsArrayPostfix:
					{
						addToken(CsTokenType.tkLBRACKET);
						for (int i = 1; i < node.rank; ++i)
						{
							addToken(CsTokenType.tkCOMMA);
						}
						addToken(CsTokenType.tkRBRACKET);
					}
					break;
				case TypeSpecifierEnum.tsPointer:
					{
						addToken(CsTokenType.tkSTAR);
					}
					break;
			}
		}

		public void callback(CsTypeSpecifiers node)
		{
			if (node.specifiers != null)
			{
				foreach (CsTypeSpecifier ts in node.specifiers)
				{
					if (ts != null)
						callback(ts);
				}
			}
		}

		public void callback(CsExternAliasDirective node)
		{
			addNewline();
			addKeyword(CsTokenType.tkUSING);
			addWS();
			addToken("alias");
			addWS();
			addToken(node.identifier);
			addSemicolon();
			addNewline();
		}
		public void callback(CsUsingAliasDirective node)
		{
			addNewline();
			addKeyword(CsTokenType.tkUSING);
			addWS();
			addToken(node.identifier);
			addOperator(CsTokenType.tkASSIGN);
			addWS();
			if (node.namespace_or_type_name != null)
				node.namespace_or_type_name.callback(this);
			addSemicolon();
			addNewline();
		}
		public void callback(CsUsingNamespaceDirective node)
		{
			addNewline();
			addKeyword(CsTokenType.tkUSING);
			addWS();
			if (node.namespace_or_type_name != null)
				node.namespace_or_type_name.callback(this);
			addSemicolon();
			addNewline();
		}
		public void callback(CsTypeRef node)
		{
			addWS();
			if (node.isTypeName())
			{
				if (node.type_name != null)
					node.type_name.callback(this);
			}
			else
				addToken((CsTokenType)node.predefined_type);
			if (node.nullable)
			{
				addToken(CsTokenType.tkQUESTION);
			}
			if (node.specifiers != null)
			{
				addWSKill();
				callback(node.specifiers);
			}
			addWS();
		}
		public void callback(CsTypeArgumentList node)
		{
			addToken(CsTokenType.tkLESS);
			bool bFirst = true;
			if (node.list != null)
			{
				foreach (CsTypeRef typeref in node.list)
				{
					if (bFirst)
						bFirst = false;
					else
						addToken(CsTokenType.tkCOMMA);
					if (typeref != null)
						typeref.callback(this);
				}
			}
			addToken(CsTokenType.tkGREATER);
		}
		public void callback(CsQualifiedAliasMember node)
		{
			if (node.qualified_alias_identifier.identifier != null)
			{
				addToken(node.qualified_alias_identifier);
				addToken(CsTokenType.tkSCOPE);
			}
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsNamespaceOrTypeName node)
		{
			if (node.namespace_or_type_name != null)
			{
				node.namespace_or_type_name.callback(this);
				addToken(CsTokenType.tkDOT);
			}
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsSimpleName node)
		{
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsLiteral node)
		{
			switch (node.type)
			{
				case CsTokenType.tkLITERAL:
					//addToken(new CsToken(CsTokenType.tkLITERAL, node.literal, (CsLiteralType)node.literal_type, LineInfo()));
					addToken(new CsToken(CsTokenType.tkLITERAL, node.literal, new LineInfo(), node.original_text));
					break;
				default:
					addToken(node.type);
					break;
			}
		}
		public void callback(CsPredefinedTypeMemberAccess node)
		{
			addToken(node.predefined_type);
			addToken(CsTokenType.tkDOT);
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsThisAccess node)
		{
			addToken(CsTokenType.tkTHIS);
		}
		public void callback(CsBaseMemberAccess node)
		{
			addToken(CsTokenType.tkBASE);
			addToken(CsTokenType.tkDOT);
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsBaseIndexerAccess node)
		{
			addToken(CsTokenType.tkBASE);
			addToken(CsTokenType.tkLBRACKET);
			if (node.expression_list != null)
				callback(node.expression_list);
			addToken(CsTokenType.tkRBRACKET);
		}
		public void callback(CsTypeofExpression node)
		{
			addKeyword(CsTokenType.tkTYPEOF);
			addToken(CsTokenType.tkLPAREN);
			if (node.type != null)
				callback(node.type);
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsCheckedExpression node)
		{
			addKeyword(CsTokenType.tkCHECKED);
			addToken(CsTokenType.tkLPAREN);
			if (node.expression != null)
				node.expression.callback(this);
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsUncheckedExpression node)
		{
			addKeyword(CsTokenType.tkUNCHECKED);
			addToken(CsTokenType.tkLPAREN);
			if (node.expression != null)
				node.expression.callback(this);
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsLockStatement node)
		{
			addKeyword(CsTokenType.tkLOCK);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (!bBlock)
					addIncTab();
				node.statement.callback(this);
				if (!bBlock)
					addDecTab();
			}
		}
		public void callback(CsSizeofExpression node)
		{
			addKeyword(CsTokenType.tkSIZEOF);
			addToken(CsTokenType.tkLPAREN);
			if (node.type != null)
				callback(node.type);
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsParenthesizedExpression node)
		{
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsMemberInitializer node)
		{
			addToken(node.identifier);
			addWS();
			addToken(CsTokenType.tkASSIGN);
			addWS();
			if (node.initializer != null)
				node.initializer.callback(this);
		}
		public void callback(CsObjectInitializer node)
		{
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			if (node.member_initializer_list != null)
			{
				bool bFirst = true;
				foreach (CsMemberInitializer initializer in node.member_initializer_list)
				{
					if (!bFirst)
						addToken(CsTokenType.tkCOMMA);
					else
						bFirst = false;
					if (initializer != null)
						initializer.callback(this);
				}
			}
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsCollectionInitializer node)
		{
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			if (node.element_initializer_list != null)
			{
				bool bFirst = true;
				foreach (CsExpression expr in node.element_initializer_list)
				{
					if (!bFirst)
						addToken(CsTokenType.tkCOMMA);
					else
						bFirst = false;
					if (expr != null)
						expr.callback(this);
				}
			}
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsNewObjectExpression node)
		{
			addKeyword(CsTokenType.tkNEW);
			if (node.type != null)
				callback(node.type);
			addToken(CsTokenType.tkLPAREN);
			if (node.argument_list != null)
			{
				addWS();
				callback(node.argument_list);
				addWS();
			}
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsNewArrayExpression node)
		{
			addKeyword(CsTokenType.tkNEW);
			if (node.type != null)
				callback(node.type);
			addWSKill();
			if (node.expressions != null)
			{
				addToken(CsTokenType.tkLBRACKET);
				callback(node.expressions);
				addToken(CsTokenType.tkRBRACKET);
			}
			if (node.specifiers != null)
			{
				addWSKill();
				foreach (CsTypeSpecifier ts in node.specifiers)
				{
					callback(ts);
				}
			}
			addWS();
			if (node.initializer != null)
				callback(node.initializer);
		}
		public void callback(CsMemberDeclarator node)
		{
			if (node.identifier.identifier != null)
			{
				addToken(node.identifier);
				addWS();
				addToken(CsTokenType.tkASSIGN);
				addWS();
			}
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsAnonymousObjectCreationExpression node)
		{
			addToken(CsTokenType.tkNEW);
			addWS();
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			addNewlineKill1();
			if (node.member_declarator_list != null)
			{
				bool bFirst = true;
				foreach (CsMemberDeclarator declarator in node.member_declarator_list)
				{
					if (!bFirst)
						addComma();
					declarator.callback(this);
					bFirst = false;
				}
			}
			addNewlineKill1();
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsInvocationExpression node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkLPAREN);
			if (node.argument_list != null
				&& node.argument_list.list != null
				&& node.argument_list.list.Count != 0)
			{
				addWS();
				callback(node.argument_list);
				addWS();
			}
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsElementAccess node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addToken(CsTokenType.tkLBRACKET);
			if (node.argument_list != null)
				callback(node.argument_list);
			addToken(CsTokenType.tkRBRACKET);
		}
		public void callback(CsPrimaryExpressionMemberAccess node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addToken(CsTokenType.tkDOT);
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsPointerMemberAccess node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addToken(CsTokenType.tkARROW);
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsPostIncrementDecrementExpression node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addToken(node.oper);
		}
		public void callback(CsBinaryExpression node)
		{
			if (node.lhs != null)
				node.lhs.callback(this);
			addWS();
			addToken(node.oper);
			addWS();
			if (node.rhs != null)
				node.rhs.callback(this);
		}
		public void callback(CsAsIsExpression node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addKeyword(node.oper);
			if (node.type != null)
				callback(node.type);
		}
		public void callback(CsConditionalExpression node)
		{
			if (node.condition != null)
				node.condition.callback(this);
			addOperator(CsTokenType.tkQUESTION);
			if (node.true_expression != null)
				node.true_expression.callback(this);
			addOperator(CsTokenType.tkCOLON);
			if (node.false_expression != null)
				node.false_expression.callback(this);
		}
		public void callback(CsOperatorUnaryExpression node)
		{
			addToken(node.oper);
			if (node.unary_expression != null)
				node.unary_expression.callback(this);
		}
		public void callback(CsPreIncrementDecrementExpression node)
		{
			addToken(node.oper);
			if (node.unary_expression != null)
				node.unary_expression.callback(this);
		}
		public void callback(CsCastUnaryExpression node)
		{
			addToken(CsTokenType.tkLPAREN);
			if (node.type != null)
				callback(node.type);
			addToken(CsTokenType.tkRPAREN);
			addWS();
			if (node.unary_expression != null)
				node.unary_expression.callback(this);
		}
		public void callback(CsAssignmentExpression node)
		{
			if (node.lhs != null)
				node.lhs.callback(this);
			addOperator(node.oper);
			if (node.rhs != null)
				node.rhs.callback(this);
		}
		public void callback(CsQualifiedAliasMemberAccess node)
		{
			if (node.qualified_alias_member != null)
				node.qualified_alias_member.callback(this);
			addToken(CsTokenType.tkDOT);
			addToken(node.identifier);
			if (node.type_argument_list != null)
				node.type_argument_list.callback(this);
		}
		public void callback(CsDefaultValueExpression node)
		{
			addKeyword(CsTokenType.tkDEFAULT);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.type != null)
				node.type.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsAnonymousMethodExpression node)
		{
			addKeyword(CsTokenType.tkDELEGATE);
			addWS();
			if (node.signature != null)
			{
				addToken(CsTokenType.tkLPAREN);
				addWS();
				node.signature.callback(this);
				addWS();
				addToken(CsTokenType.tkRPAREN);
				addWS();
			}
			if (node.block != null)
			{
				addNewline();
				node.block.callback(this);
			}
		}
		public void callback(CsExpressionListBase node)
		{
			bool bFirst = true;
			if (node.list != null)
			{
				foreach (CsExpression expr in node.list)
				{
					if (!bFirst)
						addComma();
					if (expr != null)
						expr.callback(this);
					bFirst = false;
				}
			}

		}
		public void callback(CsExpressionList node)
		{
			callback((CsExpressionListBase)node);
		}
		public void callback(CsPositionalArgumentList node)
		{
			callback((CsExpressionListBase)node);
		}
		public void callback(CsElementInitializer node)
		{
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			addNewlineKill1();
			callback((CsExpressionListBase)node);
			addNewlineKill1();
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsNamedArgument node)
		{
			addToken(node.identifier);
			addOperator(CsTokenType.tkASSIGN);
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsAttribute node)
		{
			if (node.attribute_name != null)
				node.attribute_name.callback(this);

			if (node.positional_argument_list != null
				|| node.named_argument_list != null)
			{
				addToken(CsTokenType.tkLPAREN);
			}
			bool bFirst = true;
			if (node.positional_argument_list != null)
			{
				node.positional_argument_list.callback(this);
			}
			if (node.named_argument_list != null)
			{
				foreach (CsNamedArgument na in node.named_argument_list)
				{
					if (!bFirst)
						addComma();
					if (na != null)
						na.callback(this);
					bFirst = false;
				}
			}
			if (node.positional_argument_list != null
				|| node.named_argument_list != null)
			{
				addToken(CsTokenType.tkRPAREN);
			}
		}
		public void callback(CsAttributeSection node)
		{
			addToken(CsTokenType.tkLBRACKET);
			switch (node.specifier)
			{
				case AttributeSpecifierEnum.asNONE:
				case AttributeSpecifierEnum.asILLEGAL:
					break;
				case AttributeSpecifierEnum.asASSEMBLY:
					addToken(("assembly"));
					goto colon;
				case AttributeSpecifierEnum.asEVENT:
					addToken(("event"));
					goto colon;
				case AttributeSpecifierEnum.asFIELD:
					addToken(("field"));
					goto colon;
				case AttributeSpecifierEnum.asMETHOD:
					addToken(("method"));
					goto colon;
				case AttributeSpecifierEnum.asMODULE:
					addToken(("module"));
					goto colon;
				case AttributeSpecifierEnum.asPARAM:
					addToken(("param"));
					goto colon;
				case AttributeSpecifierEnum.asPROPERTY:
					addToken(("property"));
					goto colon;
				case AttributeSpecifierEnum.asRETURN:
					addToken(("return"));
					goto colon;
				case AttributeSpecifierEnum.asTYPE:
					addToken(("type"));
					goto colon;
				case AttributeSpecifierEnum.asTYPEVAR:
					addToken(("typevar"));
					goto colon;
				default:
#if !PUBLIC_RELEASE
					Debug.Assert(false);
#else
			ASSERT(0);
#endif // !PUBLIC_RELEASE
					break;
				colon:
					addColon();
				addWS();
				break;
			}
			bool bFirst = true;
			if (node.attribute_list != null)
			{
				foreach (CsAttribute attr in node.attribute_list)
				{
					if (!bFirst)
						addComma();
					if (attr != null)
						callback(attr);
					bFirst = false;
				}
			}
			addToken(CsTokenType.tkRBRACKET);
			addNewline();
		}
		public void callback(CsAttributes node)
		{
			if (node.sections != null)
			{
				foreach (CsAttributeSection section in node.sections)
				{
					if (section != null)
						callback(section);
				}
			}
		}
		public void callback(CsTypeBase node)
		{
			addWS();
			addToken(CsTokenType.tkCOLON);
			bool bFirst = true;
			if (node.base_list != null)
			{
				foreach (CsTypeRef typeref in node.base_list)
				{
					if (!bFirst)
						addComma();
					if (typeref != null)
						callback(typeref);
					bFirst = false;
				}
			}
		}
		public void callback(CsTypeParameter node)
		{
			if (node.attributes != null)
				node.attributes.callback(this);
			if (node.variance_annotation != CsTokenType.tkEOF)
				addKeyword(node.variance_annotation);
			addToken(node.identifier);
		}
		public void callback(CsTypeParameterList node)
		{
			addToken(CsTokenType.tkLESS);
			bool bFirst = true;
			if (node.list != null)
			{
				foreach (CsTypeParameter tp in node.list)
				{
					if (bFirst)
						bFirst = false;
					else
						addToken(CsTokenType.tkCOMMA);
					if (tp != null)
						callback(tp);
				}
			}
			addToken(CsTokenType.tkGREATER);
		}
		public void callback(CsTypeParameterConstraintsClause node)
		{
			addNewline();
			addToken("where");
			addWS();
			addToken(node.type_parameter);
			addWS();
			addColon();
			addWS();
			bool bNeedComma = false;
			if ((node.constraints_flags & (uint)cs_generic_param_constaint.gpc_referencetype) != 0)
			{
				addToken(CsTokenType.tkCLASS);
				bNeedComma = true;
			}
			else if ((node.constraints_flags & (uint)cs_generic_param_constaint.gpc_valuetype) != 0)
			{
				addToken(CsTokenType.tkSTRUCT);
				bNeedComma = true;
			}
			if (node.constraints != null)
			{
				foreach (CsTypeRef typeref in node.constraints)
				{
					if (!bNeedComma)
						bNeedComma = true;
					else
						addComma();
					if (typeref != null)
						callback(typeref);
				}
			}
			else if ((node.constraints_flags & (uint)cs_generic_param_constaint.gpc_constructor) != 0)
			{
				if (bNeedComma)
					addComma();
				addToken(CsTokenType.tkNEW);
				addToken(CsTokenType.tkLPAREN);
				addToken(CsTokenType.tkRPAREN);
			}
			addNewline();
		}
		public void callback(CsClass node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addKeyword(CsTokenType.tkCLASS);
			addToken(node.identifier);
			if (node.type_parameter_list != null)
				callback(node.type_parameter_list);
			if (node.type_base != null)
				callback(node.type_base);
			if (node.constraints != null)
			{
				foreach (CsTypeParameterConstraintsClause clause in node.constraints)
				{
					addNewline();
					if (clause != null)
						clause.callback(this);
					addNewline();
				}
			}
			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			addNewlineKill1();
			if (node.member_declarations != null)
			{
				foreach (CsNode declaration in node.member_declarations)
				{
					if (declaration != null)
					{
						addNewline();
						declaration.callback(this);
						addNewline();
					}
				}
			}
			addNewlineKill1();
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline2();
		}
		public void callback(CsStruct node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addKeyword(CsTokenType.tkSTRUCT);
			addToken(node.identifier);
			if (node.type_parameter_list != null)
				callback(node.type_parameter_list);
			if (node.type_base != null)
				callback(node.type_base);
			if (node.constraints != null)
			{
				foreach (CsTypeParameterConstraintsClause clause in node.constraints)
				{
					addNewline();
					if (clause != null)
						clause.callback(this);
					addNewline();
				}
			}
			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			if (node.member_declarations != null)
			{
				foreach (CsNode member in node.member_declarations)
				{
					if (member != null)
						member.callback(this);
				}
			}
			addDecTab();
			addNewlineKill1();
			addToken(CsTokenType.tkRCURLY);
			addNewline2();
		}
		public void callback(CsInterface node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addKeyword(CsTokenType.tkINTERFACE);
			addToken(node.identifier);
			if (node.type_parameter_list != null)
				node.type_parameter_list.callback(this);
			if (node.type_base != null)
				callback(node.type_base);
			if (node.constraints != null)
			{
				foreach (CsTypeParameterConstraintsClause clause in node.constraints)
				{
					addNewline();
					if (clause != null)
						clause.callback(this);
					addNewline();
				}
			}
			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			if (node.member_declarations != null)
			{
				foreach (CsNode member in node.member_declarations)
				{
					addNewlineKill1();
					if (member != null)
						member.callback(this);
					addNewlineKill1();
				}
			}
			addDecTab();
			addNewlineKill1();
			addToken(CsTokenType.tkRCURLY);
			addNewline2();
		}
		public void callback(CsEnumMember node)
		{
			if (node.attributes != null)
				callback(node.attributes);
			addToken(node.identifier);
			if (node.expression != null)
			{
				addOperator(CsTokenType.tkASSIGN);
				node.expression.callback(this);
			}
			addComma();
			addNewline();
		}
		public void callback(CsEnum node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addKeyword(CsTokenType.tkENUM);
			addToken(node.identifier);
			if (node.underlying_type != CsTokenType.tkEOF)
			{
				addWS();
				addToken(CsTokenType.tkCOLON);
				addWS();
				addToken(node.underlying_type);
			}
			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addNewline();
			addIncTab();
			if (node.members != null)
			{

				foreach (CsEnumMember member in node.members)
				{
					if (member != null)
						callback(member);
				}
			}
			addNewline();
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline2();
		}
		public void callback(CsDefaultArgument node)
		{
			addToken(CsTokenType.tkASSIGN);
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsFormalParameter node)
		{
			if (node.attributes != null)
				callback(node.attributes);
			if (node.modifier != CsTokenType.tkEOF)
				addKeyword(node.modifier);
			if (node.type != null)
				callback(node.type);
			addToken(node.identifier);
			if (node.default_argument != null)
				node.default_argument.callback(this);
		}
		public void callback(CsFormalParameterListBase node)
		{
			bool bFirst = true;
			if (node.parameters != null)
			{
				foreach (CsFormalParameter param in node.parameters)
				{
					if (!bFirst)
						addComma();
					if (param != null)
						callback(param);
					bFirst = false;
				}
			}
		}
		public void callback(CsFormalParameterList node)
		{
			callback((CsFormalParameterListBase)node);
		}
		public void callback(CsAnonymousFunctionSignature node)
		{
			if (node.parentheses)
				addToken(CsTokenType.tkLPAREN);
			callback((CsFormalParameterListBase)node);
			if (node.parentheses)
				addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsDelegate node)
		{
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addToken(CsTokenType.tkDELEGATE);
			if (node.type != null)
				callback(node.type);
			addToken(node.identifier);
			if (node.type_parameter_list != null)
				node.type_parameter_list.callback(this);
			addWS();
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.parameters != null)
				callback(node.parameters);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			if (node.constraints != null)
			{
				foreach (CsTypeParameterConstraintsClause clause in node.constraints)
				{
					addNewline();
					if (clause != null)
						clause.callback(this);
					addNewline();
				}
			}
			addSemicolon();
		}
		public void callback(CsNamespace node)
		{
			addKeyword(CsTokenType.tkNAMESPACE);
			if (node.qualified_identifier != null)
				callback(node.qualified_identifier);
			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addNewlineKill1();
			addNewline();
			addIncTab();
			{
				if (node.using_directives != null)
				{
					foreach (CsUsingDirective ud in node.using_directives)
					{
						if (ud != null)
						{
							addNewline();
							ud.callback(this);
							addNewline();
						}
					}
				}
			}
			{
				if (node.member_declarations != null)
				{
					foreach (CsNode member in node.member_declarations)
					{
						if (member != null)
						{
							addNewline2();
							member.callback(this);
							addNewline2();
						}
					}
				}
			}
			addDecTab();
			addNewline();
			addNewlineKill1();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsArgumentName node)
		{
			addToken(node.identifier);
			addWS();
			addToken(CsTokenType.tkCOLON);
			addWS();
		}
		public void callback(CsArgument node)
		{
			if (node.argument_name != null)
				node.argument_name.callback(this);
			if (node.modifier != CsTokenType.tkEOF)
				addKeyword(node.modifier);
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsArgumentList node)
		{
			bool bFirst = true;
			if (node.list != null)
			{
				foreach (CsArgument arg in node.list)
				{
					if (!bFirst)
						addComma();
					if (arg != null)
						callback(arg);
					bFirst = false;
				}
			}
		}
		public void callback(CsBlock node)
		{
			addToken(CsTokenType.tkLCURLY);
			addIncTab();
			if (node.statements != null)
			{
				foreach (CsStatement stat in node.statements)
				{
					addNewline();
					if (stat != null)
						stat.callback(this);
					addNewline();
				}
			}
			addDecTab();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsLabel node)
		{
			addToken(node.identifier);
			addColon();
			addNewline();
		}
		public void callback(CsLabeledStatement node)
		{
			if (node.labels != null)
			{
				addDecTab();
				foreach (CsLabel label in node.labels)
				{
					label.callback(this);
				}
				addIncTab();
			}
			if (node.statement != null)
				node.statement.callback(this);
		}
		public void callback(CsEmptyStatement node)
		{
			addSemicolon();
		}
		public void callback(CsExpressionStatement node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			addSemicolon();
		}
		public void callback(CsDeclarationStatement node)
		{
			if (node.declaration != null)
				node.declaration.callback(this);
			addSemicolon();
		}
		public void callback(CsLocalConstantDeclarator node)
		{
			if (node.identifier.identifier != null)
				addToken(node.identifier);
			if (node.expression != null)
			{
				addOperator(CsTokenType.tkASSIGN);
				node.expression.callback(this);
			}
		}
		public void callback(CsLocalConstantDeclaration node)
		{
			addKeyword(CsTokenType.tkCONST);
			if (node.type != null)
				callback(node.type);
			bool bFirst = true;
			if (node.declarators != null)
			{
				foreach (CsLocalConstantDeclarator declarator in node.declarators)
				{
					if (!bFirst)
						addComma();
					if (declarator != null)
						callback(declarator);
					bFirst = false;
				}
			}
		}
		public void callback(CsIfStatement node)
		{
			addKeyword(CsTokenType.tkIF);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.condition != null)
				node.condition.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			if (node.if_statement != null)
			{
				addNewline();
				bool bBlock = (node.if_statement.e == cs_node.n_block);
				if (!bBlock)
					addIncTab();
				node.if_statement.callback(this);
				if (!bBlock)
					addDecTab();
				addNewline();
			}
			if (node.else_statement != null)
			{
				addToken(CsTokenType.tkELSE);
				bool bIfStatement = (node.else_statement.e == cs_node.n_if_statement);
				if (!bIfStatement)
					addNewline();
				bool bBlock = (node.else_statement.e == cs_node.n_block);
				if (!bBlock && !bIfStatement)
					addIncTab();
				node.else_statement.callback(this);
				if (!bBlock && !bIfStatement)
					addDecTab();
				addNewline();
			}
		}
		public void callback(CsSwitchLabel node)
		{
			addNewline();
			if (node.expression != null)
			{
				addKeyword(CsTokenType.tkCASE);
				if (node.expression != null)
					node.expression.callback(this);
				addColon();
			}
			else
			{
				addKeyword(CsTokenType.tkDEFAULT);
				addColon();
			}
			addNewline();
		}
		public void callback(CsSwitchSection node)
		{
			if (node.labels != null)
			{
				foreach (CsSwitchLabel label in node.labels)
				{
					if (label != null)
						callback(label);
				}
			}
			addIncTab();
			if (node.statements != null)
			{
				foreach (CsStatement stat in node.statements)
				{
					if (stat != null)
					{
						addNewline();
						stat.callback(this);
						addNewline();
					}
				}
			}
			addDecTab();
		}
		public void callback(CsSwitchStatement node)
		{
			addKeyword(CsTokenType.tkSWITCH);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addNewline();
			addIncTab();
			if (node.sections != null)
			{
				foreach (CsSwitchSection section in node.sections)
				{
					if (section != null)
						callback(section);
				}
			}
			addDecTab();
			addNewline();
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsWhileStatement node)
		{
			addKeyword(CsTokenType.tkWHILE);
			addWS();
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.condition != null)
				node.condition.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (node.statement != null)
				{
					addNewline();
					if (!bBlock)
						addIncTab();
					node.statement.callback(this);
					if (!bBlock)
						addDecTab();
				}
			}
		}
		public void callback(CsDoWhileStatement node)
		{
			addKeyword(CsTokenType.tkDO);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (!bBlock)
					addIncTab();
				node.statement.callback(this);
				if (!bBlock)
					addDecTab();
			}
			addNewline();
			addKeyword(CsTokenType.tkWHILE);
			addToken(CsTokenType.tkLPAREN);
			if (node.condition != null)
			{
				addWS();
				node.condition.callback(this);
				addWS();
			}
			addToken(CsTokenType.tkRPAREN);
			addSemicolon();
		}
		public void callback(CsLocalVariableDeclarator node)
		{
			addToken(node.identifier);
			if (node.initializer != null)
			{
				addOperator(CsTokenType.tkASSIGN);
				node.initializer.callback(this);
			}
		}
		public void callback(CsLocalVariableDeclaration node)
		{
			if (node.type != null)
				node.type.callback(this);
			bool bFirst = true;
			if (node.declarators != null)
			{
				foreach (CsLocalVariableDeclarator declarator in node.declarators)
				{
					if (!bFirst)
						addComma();
					if (declarator != null)
						callback(declarator);
					bFirst = false;
				}
			}
			addSemicolon();
		}
		public void callback(CsLambdaExpression node)
		{
			if (node.signature != null)
				node.signature.callback(this);
			addToken(CsTokenType.tkFOLLOW);
			if (node.body != null)
				node.body.callback(this);
		}
		public void callback(CsJoinClause node)
		{
			addNewline();
			addToken("join");
			addWS();
			if (node.type != null)
			{
				node.type.callback(this);
				addWS();
			}
			if (node.identifier.identifier != null)
				addToken(node.identifier);
			addKeyword(CsTokenType.tkIN);
			if (node.in_expression != null)
				node.in_expression.callback(this);
			addWS();
			addToken("on");
			addWS();
			if (node.on_expression != null)
				node.on_expression.callback(this);
			addWS();
			addToken("equals");
			addWS();
			if (node.equals_expression != null)
				node.equals_expression.callback(this);
			if (node.into_identifier.identifier != null)
			{
				addWS();
				addToken("into");
				addWS();
				addToken(node.into_identifier);
			}
		}
		public void callback(CsFromClause node)
		{
			addNewline();
			addToken("from");
			addWS();
			if (node.type != null)
			{
				node.type.callback(this);
				addWS();
			}
			if (node.identifier.identifier != null)
				addToken(node.identifier);
			addKeyword(CsTokenType.tkIN);
			if (node.expression != null)
				node.expression.callback(this);
			//if (node.join_clauses != null)
			//{
			//  foreach (CsJoinClause clause in node.join_clauses)
			//  {
			//    callback(clause);
			//  }
			//}
		}
		public void callback(CsLetClause node)
		{
			addNewline();
			addToken("let");
			addWS();
			if (node.identifier.identifier != null)
			{
				addToken(node.identifier);
				addWS();
			}
			addToken(CsTokenType.tkASSIGN);
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsWhereClause node)
		{
			addNewline();
			addToken("where");
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsOrdering node)
		{
			if (node.expression != null)
				node.expression.callback(this);
			switch (node.ordering_direction)
			{
				case CsOrderingDirection.ordering_direction_ascending:
					addToken("ascending");
					break;
				case CsOrderingDirection.ordering_direction_descending:
					addToken("descending");
					break;
			}
		}
		public void callback(CsOrderbyClause node)
		{
			addNewline();
			addToken("orderby");
			if (node.orderings != null)
			{
				bool bFirst = true;
				foreach (CsOrdering ordering in node.orderings)
				{
					if (!bFirst)
						addComma();
					else
						bFirst = false;
					if (ordering != null)
						callback(ordering);
				}
			}
		}
		public void callback(CsSelectClause node)
		{
			addNewline();
			addToken("select");
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsGroupClause node)
		{
			addNewline();
			addToken("group");
			if (node.group_expression != null)
			{
				addWS();
				node.group_expression.callback(this);
			}
			addWS();
			addToken("by");
			if (node.by_expression != null)
			{
				addWS();
				node.by_expression.callback(this);
			}
		}
		public void callback(CsQueryContinuation node)
		{
			addToken("into");
			if (node.identifier.identifier != null)
			{
				addWS();
				addToken(node.identifier);
			}
			//if (node.join_clauses != null)
			//{
			//  bool bFirst = true;
			//  foreach (CsJoinClause clause in node.join_clauses)
			//  {
			//    if (!bFirst)
			//      addComma();
			//    else
			//      bFirst = false;
			//    if (clause != null)
			//      callback(clause);
			//  }
			//}
			if (node.query_body != null)
				callback(node.query_body);
		}
		public void callback(CsQueryBody node)
		{
			if (node.query_body_clauses != null)
			{
				bool bFirst = true;
				foreach (CsNode clause in node.query_body_clauses)
				{
					if (!bFirst)
						addComma();
					else
						bFirst = false;
					if (clause != null)
						clause.callback(this);
				}
			}
			if (node.select_or_group_clause != null)
			{
				node.select_or_group_clause.callback(this);
			}
			if (node.query_continuation != null)
			{
				callback(node.query_continuation);
			}
		}
		public void callback(CsQueryExpression node)
		{
			if (node.from_clause != null)
				callback(node.from_clause);
			if (node.query_body != null)
				callback(node.query_body);
		}
		public void callback(CsStatementExpressionList node)
		{
			bool bFirst = true;
			if (node.expressions != null)
			{
				foreach (CsExpression expr in node.expressions)
				{
					if (!bFirst)
						addComma();
					if (expr != null)
						expr.callback(this);
					bFirst = false;
				}
			}
		}
		public void callback(CsForStatement node)
		{
			addKeyword(CsTokenType.tkFOR);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.initializer != null)
			{
				node.initializer.callback(this);
				addSemicolon();
			}
			else
			{
				addToken(CsTokenType.tkSEMICOLON);
			}
			addWS();
			if (node.condition != null)
				node.condition.callback(this);
			addSemicolon();
			addWS();
			if (node.iterator != null)
				node.iterator.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (node.statement != null)
				{
					if (!bBlock)
						addIncTab();
					node.statement.callback(this);
					if (!bBlock)
						addDecTab();
				}
			}
		}
		public void callback(CsForeachStatement node)
		{
			addKeyword(CsTokenType.tkFOREACH);
			addToken(CsTokenType.tkLPAREN);
			if (node.type != null)
				callback(node.type);
			addToken(node.identifier);
			addKeyword(CsTokenType.tkIN);
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (node.statement != null)
				{
					if (!bBlock)
						addIncTab();
					node.statement.callback(this);
					if (!bBlock)
						addDecTab();
				}
			}
		}
		public void callback(CsBreakStatement node)
		{
			addKeyword(CsTokenType.tkBREAK);
			addSemicolon();
		}
		public void callback(CsContinueStatement node)
		{
			addKeyword(CsTokenType.tkCONTINUE);
			addSemicolon();
		}
		public void callback(CsGotoStatement node)
		{
			addKeyword(CsTokenType.tkGOTO);
			switch (node.label_type)
			{
				case CsTokenType.tkIDENTIFIER:
					addToken(node.label);
					break;
				case CsTokenType.tkCASE:
					addKeyword(CsTokenType.tkCASE);
					if (node.case_expression != null)
						node.case_expression.callback(this);
					break;
				case CsTokenType.tkDEFAULT:
					addToken(CsTokenType.tkDEFAULT);
					break;
			}
			addSemicolon();
		}
		public void callback(CsReturnStatement node)
		{
			addKeyword(CsTokenType.tkRETURN);
			if (node.expression != null)
				node.expression.callback(this);
			addSemicolon();
		}
		public void callback(CsThrowStatement node)
		{
			addKeyword(CsTokenType.tkTHROW);
			if (node.expression != null)
				node.expression.callback(this);
			addSemicolon();
		}
		public void callback(CsCatchClause node)
		{
			addKeyword(CsTokenType.tkCATCH);
			if (node.type != null)
			{
				addToken(CsTokenType.tkLPAREN);
				addWS();
				callback(node.type);
				addWS();
				addToken(node.identifier);
				addWS();
				addToken(CsTokenType.tkRPAREN);
			}
			addNewline();
			if (node.block != null)
				callback(node.block);
		}
		public void callback(CsTryStatement node)
		{
			addKeyword(CsTokenType.tkTRY);
			addNewline();
			if (node.try_block != null)
				callback(node.try_block);
			if (node.catch_clauses != null)
			{
				foreach (CsCatchClause clause in node.catch_clauses)
				{
					addNewline();
					if (clause != null)
						callback(clause);
				}
			}
			if (node.finally_block != null)
			{
				addNewline();
				addKeyword(CsTokenType.tkFINALLY);
				addNewline();
				callback(node.finally_block);
			}
		}
		public void callback(CsCheckedStatement node)
		{
			addKeyword(CsTokenType.tkCHECKED);
			addNewline();
			if (node.block != null)
				callback(node.block);
		}
		public void callback(CsUncheckedStatement node)
		{
			addKeyword(CsTokenType.tkUNCHECKED);
			addNewline();
			if (node.block != null)
				callback(node.block);
		}
		public void callback(CsUsingStatement node)
		{
			addKeyword(CsTokenType.tkUSING);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.resource != null)
				node.resource.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (node.statement != null)
				{
					if (!bBlock)
						addIncTab();
					node.statement.callback(this);
					if (!bBlock)
						addDecTab();
				}
			}
		}
		public void callback(CsUnsafeStatement node)
		{
			addKeyword(CsTokenType.tkUNSAFE);
			addNewline();
			if (node.block != null)
				callback(node.block);
		}
		public void callback(CsFixedPointDeclarator node)
		{
			addToken(node.identifier);
			addWS();
			addOperator(CsTokenType.tkASSIGN);
			if (node.expression != null)
				node.expression.callback(this);
		}
		public void callback(CsFixedStatement node)
		{
			addKeyword(CsTokenType.tkFIXED);
			addWS();
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.type != null)
				callback(node.type);
			bool bFirst = true;
			if (node.declarators != null)
			{
				foreach (CsFixedPointDeclarator declarator in node.declarators)
				{
					if (!bFirst)
						addComma();
					if (declarator != null)
						callback(declarator);
					bFirst = false;
				}
			}
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.statement != null)
			{
				bool bBlock = (node.statement.e == cs_node.n_block);
				if (!bBlock)
					addIncTab();
				node.statement.callback(this);
				if (!bBlock)
					addDecTab();
			}
		}
		public void callback(CsYieldStatement node)
		{
			addToken(("yield"));
			addWS();
			addKeyword(node.keyword);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addSemicolon();
		}
		public void callback(CsStackAllocInitializer node)
		{
			addKeyword(CsTokenType.tkSTACKALLOC);
			addWS();
			if (node.type != null)
				callback(node.type);
			addToken(CsTokenType.tkLBRACKET);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRBRACKET);
		}
		public void callback(CsConstructor node)
		{
			if (node.definition != null
	|| node.basethis != CsTokenType.tkEOF)
			{
				addNewline2();
			}
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addToken(node.identifier);
			addWS();
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.parameters != null)
				callback(node.parameters);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			addNewline();
			if (node.basethis != CsTokenType.tkEOF)
			{
				addIncTab();
				addToken(CsTokenType.tkCOLON);
				addWS();
				addKeyword(node.basethis);
				addToken(CsTokenType.tkLPAREN);
				if (node.argument_list != null)
				{
					addWS();
					callback(node.argument_list);
					addWS();
				}
				addToken(CsTokenType.tkRPAREN);
				addDecTab();
				addNewline();
			}
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
			{
				addSemicolon();
			}
			if (node.definition != null
				|| node.basethis != CsTokenType.tkEOF)
			{
				addNewline2();
			}
		}
		public void callback(CsConstantDeclarator node)
		{
			addToken(node.identifier);
			if (node.expression != null)
			{
				addWS();
				addOperator(CsTokenType.tkASSIGN);
				addWS();
				if (node.expression != null)
					node.expression.callback(this);
			}
		}
		public void callback(CsConstantDeclaration node)
		{
			if (node.attributes != null)
				addNewline2();
			else
				addNewline();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addKeyword(CsTokenType.tkCONST);
			addWS();
			if (node.type != null)
				callback(node.type);
			addWS();
			bool bFirst = true;
			if (node.declarators != null)
			{
				foreach (CsConstantDeclarator declarator in node.declarators)
				{
					if (!bFirst)
						addComma();
					if (declarator != null)
						callback(declarator);
					bFirst = false;
				}
			}
			addSemicolon();
			if (node.attributes != null)
				addNewline2();
			else
				addNewline();
		}
		public void callback(CsVariableDeclarator node)
		{
			addToken(node.identifier);
			if (node.initializer != null)
			{
				addWS();
				addOperator(CsTokenType.tkASSIGN);
				addWS();
				if ( node.initializer!= null )
					node.initializer.callback(this);
			}
		}
		public void callback(CsVariableDeclaration node)
		{
			if (node.attributes != null)
				addNewline2();
			else
				addNewline();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addWS();
			if (node.type != null)
				callback(node.type);
			addWS();
			bool bFirst = true;
			if (node.declarators != null)
			{
				foreach (CsVariableDeclarator declarator in node.declarators)
				{
					if (!bFirst)
						addComma();
					if (declarator != null)
						callback(declarator);
					bFirst = false;
				}
			}
			addSemicolon();
			if (node.attributes != null)
				addNewline2();
			else
				addNewline();
		}
		public void callback(CsArrayInitializer node)
		{
			addToken(CsTokenType.tkLCURLY);
			bool bFirst = true;
			if (node.initializers != null)
			{
				foreach (CsNode initializer in node.initializers)
				{
					if (!bFirst)
						addComma();
					if (initializer != null)
					{
						addWS();
						initializer.callback(this);
						addWS();
					}
					bFirst = false;
				}
			}
			addToken(CsTokenType.tkRCURLY);
			addNewline();
		}
		public void callback(CsMethod node)
		{
			if (node.definition != null
				|| node.attributes != null)
			{
				addNewline2();
			}
			else
			{
				addNewline();
			}
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			if (node.return_type != null)
				callback(node.return_type);

			if (node.interface_type != null)
			{
				node.interface_type.callback(this);
				addToken(CsTokenType.tkDOT);
			}
			addToken(node.identifier);
			if (node.type_parameter_list != null)
				callback(node.type_parameter_list);

			addWS();
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.parameters != null)
				callback(node.parameters);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			if (node.constraints != null)
			{
				foreach (CsTypeParameterConstraintsClause clause in node.constraints)
				{
					addNewline();
					clause.callback(this);
					addNewline();
				}
			}
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
			{
				addSemicolon();
			}
			if (node.definition != null
				|| node.attributes != null)
			{
				addNewline2();
			}
			else
			{
				addNewline();
			}
		}
		public void callback(CsPropertyAccessor node)
		{
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			if (!node.bSet)
				addToken(("get"));
			else
				addToken(("set"));
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
			{
				addSemicolon();
			}
			addNewline();
		}
		public void callback(CsProperty node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			if (node.type != null)
				callback(node.type);

			if (node.interface_type != null)
			{
				node.interface_type.callback(this);
				addToken(CsTokenType.tkDOT);
			}
			addToken(node.identifier);

			addNewline();
			addToken(CsTokenType.tkLCURLY);
			addNewline();
			addIncTab();
			if (node.getter != null)
				callback(node.getter);
			if (node.setter != null)
				callback(node.setter);
			addDecTab();
			addNewline();
			addToken(CsTokenType.tkRCURLY);
			addNewline2();
		}
		public void callback(CsEventAccessor node)
		{
			if (node.attributes != null)
				callback(node.attributes);
			addWS();
			if (node.bAdd)
				addToken(("add"));
			else
				addToken(("remove"));
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
				addSemicolon();
			addNewline();
		}
		public void callback(CsEventDeclarator node)
		{
			if (node.interface_type != null)
			{
				node.interface_type.callback(this);
				addToken(CsTokenType.tkDOT);
			}
			addToken(node.identifier);

			bool bCurly = node.add != null || node.remove != null;
			if (node.expression == null)
			{
				addNewline();
				if (bCurly)
					addToken(CsTokenType.tkLCURLY);
				addNewline();
				addIncTab();
				if (node.add != null)
					callback(node.add);
				if (node.remove != null)
					callback(node.remove);
				addDecTab();
				addNewline();
				if (bCurly)
					addToken(CsTokenType.tkRCURLY);
			}
			else
			{
				addToken(CsTokenType.tkASSIGN);
				if (node.expression != null)
					node.expression.callback(this);
				addSemicolon();
			}
		}
		public void callback(CsEvent node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addKeyword(CsTokenType.tkEVENT);
			if (node.type != null)
				callback(node.type);
			bool bSemicolon = true;
			if (node.declarators != null)
			{
				foreach (CsEventDeclarator declarator in node.declarators)
				{
					if (declarator.add != null
						|| declarator.remove != null)
					{
						bSemicolon = false;
					}
				}
				foreach (CsEventDeclarator declarator in node.declarators)
				{
					if (declarator != null)
						callback(declarator);
				}
			}
			if (bSemicolon)
				addSemicolon();
			addNewline2();
		}
		public void callback(CsIndexer node)
		{
			addNewline2();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			if (node.type != null)
				callback(node.type);

			if (node.interface_type != null)
			{
				node.interface_type.callback(this);
				addToken(CsTokenType.tkDOT);
			}

			addToken(CsTokenType.tkTHIS);
			addWS();
			addToken(CsTokenType.tkLBRACKET);
			addWS();
			if (node.parameters != null)
				callback(node.parameters);
			addWS();
			addToken(CsTokenType.tkRBRACKET);
			bool bGetSet = node.getter != null || node.setter != null;
			if (bGetSet)
			{
				addNewline();
				addToken(CsTokenType.tkLCURLY);
				addNewline();
				addIncTab();
				if (node.getter != null)
					callback(node.getter);
				if (node.setter != null)
					callback(node.setter);
				addDecTab();
				addNewline();
				addToken(CsTokenType.tkRCURLY);
			}
			else
			{
				addSemicolon();
			}
			addNewline2();
		}
		public void callback(CsOperator node)
		{
			if (node.attributes != null
	|| node.definition != null)
			{
				addNewline2();
			}
			else
			{
				addNewline();
			}
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			if (node.type != null)
				callback(node.type);
			addKeyword(CsTokenType.tkOPERATOR);
			addOperator(node.operation);
			addToken(CsTokenType.tkLPAREN);
			if (node.parameter1 != null)
			{
				addWS();
				callback(node.parameter1);
				addWS();
			}
			if (node.parameter2 != null)
			{
				addComma();
				addWS();
				callback(node.parameter2);
				addWS();
			}
			addToken(CsTokenType.tkRPAREN);
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
			{
				addSemicolon();
			}
			if (node.attributes != null
				|| node.definition != null)
			{
				addNewline2();
			}
			else
			{
				addNewline();
			}
		}
		public void callback(CsConversionOperator node)
		{
			if (node.attributes != null
	|| node.definition != null)
			{
				addNewline2();
			}
			else
			{
				addNewline();
			}
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			switch (node.modifier)
			{
				case CsTokenType.tkEXPLICIT:
				case CsTokenType.tkIMPLICIT:
					addKeyword(node.modifier);
					break;
			}
			addKeyword(CsTokenType.tkOPERATOR);
			if (node.type != null)
				callback(node.type);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			//if ( node.type1 )
			//	callback ( node.type1 );
			//addToken ( node.id1 );
			if (node.parameter1 != null)
				callback(node.parameter1);
			addWS();
			addToken(CsTokenType.tkRPAREN);
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
			{
				addSemicolon();
			}
			if (node.attributes != null
				|| node.definition != null)
			{
				addNewline2();
			}
			else
			{
				addNewline();
			}
		}
		public void callback(CsDestructor node)
		{
			if (node.attributes != null)
				callback(node.attributes);
			addToken(CsTokenType.tkCOMPL);
			addWSKill();
			addToken(node.identifier);
			addWSKill();
			addToken(CsTokenType.tkLPAREN);
			addToken(CsTokenType.tkRPAREN);
			if (node.definition != null)
			{
				addNewline();
				callback(node.definition);
			}
			else
			{
				addSemicolon();
			}
		}
		public void callback(CsCompilationUnit node)
		{
			{
				if (node.using_directives != null)
				{
					foreach (CsUsingDirective ud in node.using_directives)
					{
						if (ud != null)
						{
							addNewline();
							ud.callback(this);
							addNewline();
						}
					}
				}
			}
			addNewline2();
			{
				if (node.attributes != null)
				{
					foreach (CsAttributes attr in node.attributes)
					{
						if (attr != null)
						{
							addNewline();
							callback(attr);
							addNewline();
						}
					}
				}
			}
			addNewline2();
			{
				if (node.declarations != null)
				{
					foreach (CsNode declaration in node.declarations)
					{
						if (declaration != null)
						{
							addNewline2();
							if (declaration != null)
								declaration.callback(this);
							addNewline2();
						}
					}
				}
			}
		}
		public void callback(CsMakeRefExpression node)
		{
			addKeyword(CsTokenType.tkMAKEREF);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsRefTypeExpression node)
		{
			addKeyword(CsTokenType.tkREFTYPE);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addWS();
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsRefValueExpression node)
		{
			addKeyword(CsTokenType.tkREFVALUE);
			addToken(CsTokenType.tkLPAREN);
			addWS();
			if (node.expression != null)
				node.expression.callback(this);
			addComma();
			if (node.type != null)
				callback(node.type);
			addWS();
			addToken(CsTokenType.tkRPAREN);
		}
		public void callback(CsArgListExpression node)
		{
			addKeyword(CsTokenType.tkARGLIST);
			if (node.argument_list != null)
			{
				addToken(CsTokenType.tkLPAREN);
				callback(node.argument_list);
				addToken(CsTokenType.tkRPAREN);
			}
		}

		public void callback(CsFixedSizeBufferDeclarator node)
		{
			addToken(node.identifier);
			addToken(CsTokenType.tkLBRACKET);
			if (node.expression != null)
			{
				node.expression.callback(this);
			}
			addToken(CsTokenType.tkRBRACKET);
		}

		public void callback(CsFixedSizeBuffer node)
		{
			if (node.attributes != null)
				addNewline2();
			else
				addNewline();
			if (node.attributes != null)
				callback(node.attributes);
			callback(node.modifiers);
			addWS();
			addKeyword(CsTokenType.tkFIXED);
			addWS();
			if (node.type != null)
				callback(node.type);
			addWS();
			if (node.declarators != null)
			{
				bool bFirst = true;
				foreach (CsFixedSizeBufferDeclarator declarator in node.declarators)
				{
					if (!bFirst)
						addComma();
					else
						bFirst = false;
					if (declarator != null)
						callback(declarator);
				}
			}
			addSemicolon();
			if (node.attributes != null)
				addNewline2();
			else
				addNewline();
		}

		void addTokens(List<CsToken> tokens)
		{
			tokens_.AddRange(tokens);
		}

		void addToken(CsToken token)
		{
			tokens_.Add(token);
		}

		void addToken(string phs)
		{
			if (phs != null)
				tokens_.Add(new CsToken(CsTokenType.tkIDENTIFIER, phs, new LineInfo(), phs));
		}

		void addToken(CsIdentifier identifier)
		{
			tokens_.Add(new CsToken(CsTokenType.tkIDENTIFIER, identifier.identifier, new LineInfo(), identifier.original_text));
		}

		void addToken(CsTokenType token_type)
		{
			tokens_.Add(new CsToken(token_type, (byte)0, new LineInfo()));
		}

		void addKeyword(CsTokenType token_type)
		{
			addTokens(keyword_around);
			tokens_.Add(new CsToken(token_type, (byte)0, new LineInfo()));
			addTokens(keyword_around);
		}

		void addOperator(CsTokenType token_type)
		{
			addTokens(operator_around);
			tokens_.Add(new CsToken(token_type, (byte)0, new LineInfo()));
			addTokens(operator_around);
		}

		void addWS()
		{
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_WS), (byte)1, new LineInfo()));
		}

		void addWSKill()
		{
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_WS_KILL), (byte)0, new LineInfo()));
		}

		void addNewline()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_NEWLINE), (byte)1, new LineInfo()));
			addWSKill();
		}

		void addNewline2()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_NEWLINE), (byte)2, new LineInfo()));
			addWSKill();
		}

		void addNewlineKill0()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_NEWLINE_KILL), (byte)0, new LineInfo()));
			addWSKill();
		}

		void addNewlineKill1()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_NEWLINE_KILL), (byte)1, new LineInfo()));
			addWSKill();
		}

		void addComma()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsTokenType.tkCOMMA), (byte)1, new LineInfo()));
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_WS), (byte)1, new LineInfo()));
		}

		void addColon()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsTokenType.tkCOLON), (byte)1, new LineInfo()));
		}

		void addSemicolon()
		{
			addWSKill();
			tokens_.Add(new CsToken((CsTokenType)(CsTokenType.tkSEMICOLON), (byte)1, new LineInfo()));
		}

		void addIncTab()
		{
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_INC_TAB), (byte)1, new LineInfo()));
		}

		void addDecTab()
		{
			tokens_.Add(new CsToken((CsTokenType)(CsFormatTokenType.tkF_DEC_TAB), (byte)1, new LineInfo()));
		}

		public string getGeneratedText()
		{
			return generated_text_.ToString();
		}

		// tokens
		private List<CsToken> tokens_;
		private StringBuilder generated_text_;
		private List<CsToken> generated_tokens_;
		private int n_tab;

		// CsCompoundIdentifier
		private List<CsToken> compound_identifier_before_after;
		private List<CsToken> compound_identifier_around_dot;
		private List<CsToken> keyword_around;
		private List<CsToken> operator_around;

		// options
		private LineEnum end_of_line_type_;
		private bool storeNewlines_;
		private generation_option generation_option_;
		private const int tab_token_buffer_size_start_ = 8;
		private int tab_token_buffer_size_;
		private string tab_token_buffer_;
		private const int ws_token_buffer_size_start_ = 8;
		private int ws_token_buffer_size_;
		private string ws_token_buffer_;


        public void callback(CsAwaitExpression node)
        {
            throw new NotImplementedException();
        }
    };

	//------------------------------------------------------------------------------

} // namespace Metaspec
